package com.qianfeng.smartplatform.service.impl;

//
//                            _ooOoo_  
//                           o8888888o  
//                           88" . "88  
//                           (| -_- |)  
//                            O\ = /O  
//                        ____/`---'\____  
//                      .   ' \\| |// `.  
//                       / \\||| : |||// \  
//                     / _||||| -:- |||||- \  
//                       | | \\\ - /// | |  
//                     | \_| ''\---/'' | |  
//                      \ .-\__ `-` ___/-. /  
//                   ___`. .' /--.--\ `. . __  
//                ."" '< `.___\_<|>_/___.' >'"".  
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |  
//                 \ \ `-. \_ __\ /__ _/ .-` / /  
//         ======`-.____`-.___\_____/___.-`____.-'======  
//                            `=---='  
//  
//         .............................................  
//                  佛祖镇楼                  BUG辟易  
//          佛曰:  
//                  写字楼里写字间，写字间里程序员；  
//                  程序人员写程序，又拿程序换酒钱。  
//                  酒醒只在网上坐，酒醉还来网下眠；  
//                  酒醉酒醒日复日，网上网下年复年。  
//                  但愿老死电脑间，不愿鞠躬老板前；  
//                  奔驰宝马贵者趣，公交自行程序员。  
//                  别人笑我忒疯癫，我笑自己命太贱；  
//  


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qianfeng.smartplatform.cache.CategoryCache;
import com.qianfeng.smartplatform.cache.SceneCache;
import com.qianfeng.smartplatform.events.CommandEvent;
import com.qianfeng.smartplatform.exceptions.AddDataException;
import com.qianfeng.smartplatform.exceptions.QueryDataException;
import com.qianfeng.smartplatform.exceptions.ResultCode;
import com.qianfeng.smartplatform.mapper.DeviceMapper;
import com.qianfeng.smartplatform.pojo.*;
import com.qianfeng.smartplatform.service.DeviceService;
import com.qianfeng.smartplatform.utils.SecurityUtils;
import com.qianfeng.smartplatform.websocket.MyCommandHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * Created by Jackiechan on 2022/1/5/11:11
 *
 * @author Jackiechan
 * @version 1.0
 * @since 1.0
 */
@Service
@Transactional
public class DeviceServiceImpl implements DeviceService {

    private DeviceMapper deviceMapper;


    private ObjectMapper objectMapper;

    private CategoryCache categoryCache;
    private SceneCache sceneCache;


    private ApplicationContext context;

    @Autowired
    public void setContext(ApplicationContext context) {
        this.context = context;
    }

    @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Autowired
    public void setCategoryCache(CategoryCache categoryCache) {
        this.categoryCache = categoryCache;
    }

    @Autowired
    public void setSceneCache(SceneCache sceneCache) {
        this.sceneCache = sceneCache;
    }

    @Autowired
    public void setDeviceMapper(DeviceMapper deviceMapper) {
        this.deviceMapper = deviceMapper;
    }

    @Override
    public void addDevice(Device device) throws ExecutionException {
        //数据校验,必须的数据,主要就是场景id 设备的id
        //正常来说设备在出售之前是一个产品,产品在出现的时候就已经有名字和分类还有id了,根据设备的id是可以查询到这些数据的
        //但是我们精简掉了这几个东西,所以传递的时候需要传递分类id和设备名字过来
        Assert.isTrue(!device.isEmpty(CheckType.ADD), () -> {
            throw new AddDataException("必须传递的数据不完整,请检查", ResultCode.DATA_NULL);
        });
        //查询设别是不是被绑定了(应该还有解绑的功能)

        Device currentDevice = deviceMapper.selectBuId(device.getDeviceId());

        Assert.isTrue(currentDevice == null || currentDevice.getBindUserId() == device.getBindUserId(), () -> {
//            if (currentDevice.getBindUserId() == device.getBindUserId()) {
//                throw  new AddDataException()
//            }else{
//                return null;
//            }
            throw new AddDataException("设备已经被绑定到其它帐号", ResultCode.DEVICE_ALREADY_BIND);
        });
      //  User user = SecurityUtils.getUser();
        device.setBindUserId(12L);
        device.setIsOnline(2L);
        //我们发现传递过来的场景和分类不一定可靠,需要判断,如果分类和名字是内部查询的则无所谓
        //先根据传递的分类的id查询分类,看看是否存在, 因为用户可能多个手机设备,一个先添加选择了场景但是不提交,另外一个删除了场景,然后再回来提交数据,就出问题
        //分类和场景都在缓存中,所以可以从缓存中获取
        Category category = categoryCache.getValue(device.getCategyId());
        Assert.notNull(category, () -> {
            throw new AddDataException("分类不存在", ResultCode.DATA_NOT_EXIST);
        });
        device.setSceneId(device.getSceneId() == null ? -1 : device.getSceneId());
        //获取到用户的场景
        List<Scene> sceneList = sceneCache.getSceneLoadingCache().get((12L));


        if (device.getSceneId() != -1) {
            //传递了场景
            //传递的场景一定在用户的场景中
//
//            List<Long> scenedIds = new ArrayList<>();
//            for (Scene scene : sceneList) {
//                scenedIds.add(scene.getSceneId());
//            }
//            scenedIds.contains(device.getSceneId());

            Assert.isTrue(sceneList.stream().map(Scene::getSceneId).collect(Collectors.toList()).contains(device.getSceneId()), () -> {
                throw new AddDataException("场景不存在", ResultCode.DATA_NOT_EXIST);
            });
        }

        device.setBindTime(new Date());//绑定时间
        deviceMapper.addDevice(device);
    }

    @Override
    public Device findById(String id) {
        Assert.notNull(id,()->{
            throw new QueryDataException("没有传递主键", ResultCode.ID_NULL);
        });
        Device device = deviceMapper.selectBuId(id);
        return device;
    }

    @Override
    public void sendControl(String id, String command) throws JsonProcessingException {
        //我们能不能向其他人的设备发送命令
        //我们需要先检查当前设备绑定的用户是谁
        Device device = findById(id);
        //设备必须存在
        Assert.notNull(device,()->{
            throw new QueryDataException("没有对应的设备", ResultCode.DEVICE_NOT_EXIST);
        });
        BaseUser user = SecurityUtils.getUser();
        //比较绑定是不是自己
        Assert.isTrue(device.getBindUserId()==user.getUserId(),()->{
            throw new QueryDataException("当前设备不属于你", ResultCode.DEVICE_CONTROL_NOTALLOWED);
        });
        Assert.isTrue(device.getIsOnline()==1,()->{
            throw new QueryDataException("设备不在线", ResultCode.DEVICE_OFFLINE);
        });
        //根据传递过来的期望的操作来决定发送什么命令
            //我们发现命令属于分类.所以要先从分类中获取到所有的命令
        Long categyId = device.getCategyId();
        Category category = categoryCache.getValue(categyId);
        String txCommand = category.getTxCommand();//获取到要发送的命令的字符串,json格式的
        Map commandMap = objectMapper.readValue(txCommand, Map.class);//解析命令
        //根据传递的命令作为key从map中获取命令
        Map targetCommandMap = (Map) commandMap.get(command);
        Object finalCommand = targetCommandMap.get("command");//这是最终要发送给设备的命令

       // context.publishEvent(new CommandEvent(id, finalCommand));//通过事件把命令发送出去
        MyCommandHandler.sendControl(new CommandEvent(id, finalCommand));
    }

    /**
     * 这个操作是我们内部执行的,所以数据理论上不会缺失,万一缺失就是你代码写的有问题,因为数据不是用户传递的
     * @param device
     */
    @Override
    public void updateDeviceOnlineStaus(Device device) {
        if (device.getIsOnline() == 1) {
            deviceMapper.updateOnLine(device);
        }else{
            deviceMapper.updateOffLine(device);
        }
    }
}
